package com.dc.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dc.common.DateUtil;
import com.dc.common.ResponseResultVO;
import com.dc.constat.SystemConfigConstant;
import com.dc.entity.*;
import com.dc.entity.ro.StatisticalQueryRO;
import com.dc.entity.ro.assignRO;
import com.dc.entity.vo.*;
import com.dc.enums.DeviceStatusType;
import com.dc.enums.OrderStatusType;
import com.dc.enums.OrderType;
import com.dc.enums.OrderTypeEng;
import com.dc.feign.SystemApiServiceFeign;
import com.dc.mapper.*;
import com.dc.model.Device;
import com.dc.model.Dictionary;
import com.dc.ro.PageRO;
import com.dc.service.StatisticalQueryService;
import com.dc.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;

import static com.dc.common.ResponseResultVO.*;

@Service
@Slf4j
public class StatisticalQueryServiceImpl implements StatisticalQueryService {

    @Resource
    private WorkOrderMapper workOrderMapper;
    @Resource
    private WorkOrderFileMapper workOrderFileMapper;
    @Resource
    private MaintenanceRelationMapper relationMapper;
    @Resource
    private InspectionWorkOrderRelationMapper workOrderRelationMapper;
    @Resource
    private WareHouseMapper wareHouseMapper;
    @Resource
    private SystemApiServiceFeign systemApiServiceFeign;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private DeviceMapper deviceMapper;


    @Override
    public ResponseResultVO selectIncompleteOrder(StatisticalQueryRO ro) {
        Page page = new Page<Map<String, Object>>(ro.getCurrentPage(), ro.getPageSize());
        QueryWrapper<WorkOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().and(wrapper -> wrapper.between(WorkOrder::getStatus, 0, 1).between(WorkOrder::getType, 1, 4)); // 工单类型 1:维保 2:报修 3:隐患 4:巡检 5:火警
        //如果传了区域信息，因为一个巡检工单对应多个点，就先查关联当前区域的点位，拿到点位所属的巡检工单id做条件，查出来包含当前点位区域的巡检工单。
        if (StringUtils.isNotBlank(ro.getRegionName())) {
            queryWrapper.clear(); //工单类型 1:维保 2:报修 3:隐患 4:巡检 5:火警"
            queryWrapper.lambda().and(wrapper -> wrapper.between(WorkOrder::getStatus, 0, 1).between(WorkOrder::getType, 1, 3)
                    .eq(WorkOrder::getRegionName, ro.getRegionName()));
            List<InspectionWorkOrderRelation> inspectionWorkOrderRelations = workOrderRelationMapper.selectList(new QueryWrapper<InspectionWorkOrderRelation>().lambda()
                    .eq(InspectionWorkOrderRelation::getRegionName, ro.getRegionName()));
            if (CollectionUtils.isNotEmpty(inspectionWorkOrderRelations)) {
                Set<Long> workOderIds = inspectionWorkOrderRelations.stream().map(InspectionWorkOrderRelation::getWordOrderId).collect(Collectors.toSet());
                queryWrapper.lambda().or().in(WorkOrder::getId, workOderIds);
            }
        }
        queryWrapper.lambda().orderByDesc(WorkOrder::getCreateTime);
        Page<WorkOrder> selectPage = workOrderMapper.selectPage(page, queryWrapper);
        List<WorkOrder> records = selectPage.getRecords();
        List<MaintenanceRelation> maintenanceRelations = null;
        List<WorkOrderFile> workOrderFiles = null;
        if (CollectionUtils.isNotEmpty(records)) {
            Set<Long> collect = records.stream().filter(s -> 1 == s.getType()).map(WorkOrder::getId).collect(Collectors.toSet());
            if (CollectionUtils.isNotEmpty(collect)) {
                maintenanceRelations = relationMapper.selectList(new QueryWrapper<MaintenanceRelation>().lambda()
                        .in(MaintenanceRelation::getMaintenanceWorkId, collect));
            }
            Set<Long> longSet = records.stream().filter(s -> 2 == s.getType() || 3 == s.getType()).map(WorkOrder::getId).collect(Collectors.toSet());
            if (CollectionUtils.isNotEmpty(longSet)) {
                workOrderFiles = workOrderFileMapper.selectList(new QueryWrapper<WorkOrderFile>().lambda()
                        .in(WorkOrderFile::getWorkOrderId, longSet));
            }

        }

        for (WorkOrder dto : records) {
            if (OrderType.MAINTENANCE.equalsId(dto.getType())) {
                dto.setMaintenanceInfo(maintenanceRelations.stream().filter(s -> s.getMaintenanceWorkId().equals(dto.getId())).collect(Collectors.toList()));
            }
            if (OrderType.REPAIR.equalsId(dto.getType()) || OrderType.HIDDEN.equalsId(dto.getType())) {
                dto.setFiles(workOrderFiles.stream().filter(s -> s.getWorkOrderId().equals(dto.getId())).collect(Collectors.toList()));
            }
        }
        return ResponseResultVO.resultList(selectPage, ro);
    }

    @Override
    public ResponseResultVO selectWorkbenchCount(StatisticalQueryRO ro) {
        WorkOrderStatisticsVO workOrderStatisticsVO = new WorkOrderStatisticsVO();
        //巡检
        final CountDownLatch count = new CountDownLatch(1);
        ThreadPoolUtil.execute(() -> {
            QueryWrapper<WorkOrder> inspectionQueryWrapper = new QueryWrapper<>();
            if (StringUtils.isNotBlank(ro.getRegionName())) {
                List<InspectionWorkOrderRelation> inspectionWorkOrderRelations = workOrderRelationMapper.selectList(new QueryWrapper<InspectionWorkOrderRelation>().lambda().eq(InspectionWorkOrderRelation::getRegionName, ro.getRegionName()));
                if (CollectionUtils.isNotEmpty(inspectionWorkOrderRelations)) {
                    Set<Long> workOderIds = inspectionWorkOrderRelations.stream().map(InspectionWorkOrderRelation::getWordOrderId).collect(Collectors.toSet());
                    inspectionQueryWrapper.lambda().in(WorkOrder::getId, workOderIds);
                }
            }
            inspectionQueryWrapper.select(
                    "IFNULL(SUM(need_num),0) needNum",
                    "IFNULL(SUM(already_num),0) alreadyNum"
            );
            WorkOrder inspectionWorkOrder = workOrderMapper.selectOne(inspectionQueryWrapper);
            workOrderStatisticsVO.setInspectionMustNum(inspectionWorkOrder.getNeedNum());
            workOrderStatisticsVO.setInspectionFinishNum(inspectionWorkOrder.getAlreadyNum());
            count.countDown();
        });
        //报修/维保/隐患
        List<WorkOrderCountVO> workOrderCountVOS = workOrderMapper.selectWorkbenchCount(ro.getRegionName());
        for (WorkOrderCountVO vo : workOrderCountVOS) {
            Integer type = vo.getType();
            Integer status = vo.getStatus();
            switch (OrderType.getId(type)) {
                case MAINTENANCE:
                    if (status == OrderStatusType.PENDING.getId()) {
                        workOrderStatisticsVO.setMaintenancePendingNum(vo.getNumber());
                    } else if (status == OrderStatusType.PROCESS.getId()) {
                        workOrderStatisticsVO.setMaintenanceProcessNum(vo.getNumber());
                    } else if (status == OrderStatusType.FINISH.getId()) {
                        workOrderStatisticsVO.setMaintenanceFinishNum(vo.getNumber());
                    }
                    break;
                case REPAIR:
                    if (status == OrderStatusType.PENDING.getId()) {
                        workOrderStatisticsVO.setRepairPendingNum(vo.getNumber());
                    } else if (status == OrderStatusType.PROCESS.getId()) {
                        workOrderStatisticsVO.setRepairProcessNum(vo.getNumber());
                    } else if (status == OrderStatusType.FINISH.getId()) {
                        workOrderStatisticsVO.setRepairFinishNum(vo.getNumber());
                    }
                    break;
                case HIDDEN:
                    if (status == OrderStatusType.PENDING.getId()) {
                        workOrderStatisticsVO.setHiddenPendingNum(vo.getNumber());
                    } else if (status == OrderStatusType.PROCESS.getId()) {
                        workOrderStatisticsVO.setHiddenProcessNum(vo.getNumber());
                    } else if (status == OrderStatusType.FINISH.getId()) {
                        workOrderStatisticsVO.setHiddenFinishNum(vo.getNumber());
                    }
                    break;
                default:
                    log.error("未找到对应类型");
                    break;
            }
        }
        try {
            count.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return successResult(workOrderStatisticsVO);
    }

    @Override
    public ResponseResultVO selectWorkOrderDynamics(PageRO ro) {
        Page page = new Page<Map<String, Object>>(ro.getCurrentPage(), ro.getPageSize());
        Page<WorkOrder> selectPage = workOrderMapper.selectPage(page, new QueryWrapper<WorkOrder>().lambda().in(WorkOrder::getType, 1, 2, 3, 5).ne(WorkOrder::getStatus, 2).orderByDesc(WorkOrder::getCreateTime));
        List<WorkOrderDynamicsVO> workOrderDynamicsVOS = ListBeanUtils.listCopyMapper(selectPage.getRecords(), WorkOrder.class, WorkOrderDynamicsVO.class);
        return ResponseResultVO.resultPageList(workOrderDynamicsVOS, ro);
    }

    @Override
    public ResponseResultVO selectOrderDetailByIdAndType(String id) {
        WorkOrder workOrder = workOrderMapper.selectById(id);
        List<WorkOrderFile> workOrderFiles = workOrderFileMapper.selectList(new QueryWrapper<WorkOrderFile>().lambda()
                .eq(WorkOrderFile::getWorkOrderId, workOrder.getId()));
        Integer type = workOrder.getType();
        switch (OrderType.getId(type)) {
            case FIRE:
                FireVO fireVO = new FireVO();
                BeanUtils.copyProperties(workOrder, fireVO);
                return successResult(fireVO);
            case MAINTENANCE:
                MaintenanceVO maintenanceVO = new MaintenanceVO();
                BeanUtils.copyProperties(workOrder, maintenanceVO);
                List<MaintenanceRelation> maintenanceRelations = relationMapper.selectList(new QueryWrapper<MaintenanceRelation>().lambda()
                        .eq(MaintenanceRelation::getMaintenanceWorkId, workOrder.getId()));
                maintenanceVO.setInfo(maintenanceRelations);
                return successResult(maintenanceVO);
            case REPAIR:
                RepairVO repairWorkOrder = new RepairVO();
                BeanUtils.copyProperties(workOrder, repairWorkOrder);
                repairWorkOrder.setFiles(workOrderFiles);
                return successResult(repairWorkOrder);
            case HIDDEN:
                HiddenDangerVO hiddenDangerVO = new HiddenDangerVO();
                BeanUtils.copyProperties(workOrder, hiddenDangerVO);
                hiddenDangerVO.setFiles(workOrderFiles);
                return successResult(hiddenDangerVO);
            default:
                log.error("未找到对应类型");
                break;
        }
        return successResult("成功");
    }

    @Override
    public ResponseResultVO selectDataDistribution() {
        List<DataDistributionVO> result = new ArrayList<>();
        QueryWrapper<WorkOrder> inspectionQueryWrapper = new QueryWrapper<>();
        inspectionQueryWrapper.select(
                "1 as type",
                "case type when '1' then '维保' when '2' then '报修'  when '3' then '隐患'  when '4' then '巡检'  when '5' then '火警' end as name",
                "IFNULL(COUNT(*),0) as num"
        );
        inspectionQueryWrapper.lambda().groupBy(WorkOrder::getType);
        List<Map<String, Object>> maps = workOrderMapper.selectMaps(inspectionQueryWrapper);
        List<DataDistributionVO> dataDistributionVOS = Collections.EMPTY_LIST;
        DataDistributionVO dataDistributionVO = new DataDistributionVO();
        dataDistributionVOS = BeanToMapUtil.mapToBeanList(maps, dataDistributionVO);
        result.addAll(dataDistributionVOS);
        QueryWrapper<WareHouse> queryWrapper = new QueryWrapper<>();
        queryWrapper.select(
                "2 as type",
                "grade as name",
                "IFNULL(sum(stock_num),0) as num"
        );
        queryWrapper.lambda().groupBy(WareHouse::getGrade);
        List<Map<String, Object>> wareHouseMap = wareHouseMapper.selectMaps(queryWrapper);

        List<DataDistributionVO> distributionVOS = new ArrayList<>();
        distributionVOS = BeanToMapUtil.mapToBeanList(wareHouseMap, dataDistributionVO);
        result.addAll(distributionVOS);
        return successResult(result);
    }

    @Override
    public ResponseResultVO selectDataCompletionRate() {
        final CountDownLatch count = new CountDownLatch(3);
        DataCompletionRateVO dataCompletionRateVO = new DataCompletionRateVO();
        ThreadPoolUtil.execute(() -> {
            Integer pendingNum = workOrderMapper.selectCount(new QueryWrapper<WorkOrder>().lambda().between(WorkOrder::getStatus, 0, 1).ne(WorkOrder::getType, 4));
            dataCompletionRateVO.setPendingNum(pendingNum);
            String startTime = systemApiServiceFeign.selectConfigValue(SystemConfigConstant.system_running_time);
            dataCompletionRateVO.setDayNum(DateUtil.differenceNumber(startTime, DateUtil.getStartTime("start")) + 1);
            dataCompletionRateVO.setAlarmNum(workOrderMapper.selectCount(new QueryWrapper<WorkOrder>().lambda().in(WorkOrder::getType, 2, 5).ne(WorkOrder::getStatus, 2).eq(WorkOrder::getRepairType, 1)));
            count.countDown();
        });
        ThreadPoolUtil.execute(() -> {
            QueryWrapper<WorkOrder> queryWrapper = new QueryWrapper<>();
            queryWrapper.select(
                    "COUNT(*) as needNum",
                    "sum(case status WHEN 2 then 1 WHEN 1 then 1  ELSE 0 end) as alreadyNum"
            );
            queryWrapper.lambda().ne(WorkOrder::getType, 4);
            WorkOrder workOrder = workOrderMapper.selectOne(queryWrapper);
            BigDecimal a = new BigDecimal((float) workOrder.getAlreadyNum() / workOrder.getNeedNum());
            dataCompletionRateVO.setOrderHandleRate(a.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
            count.countDown();
        });
        ThreadPoolUtil.execute(() -> {
            QueryWrapper<WorkOrder> queryWrapper = new QueryWrapper<>();
            queryWrapper.select(
                    "COUNT(*) as needNum",
                    "sum(case status WHEN 2 then 1 ELSE 0 end) as alreadyNum"
            );
            queryWrapper.lambda().eq(WorkOrder::getType, 4);
            WorkOrder workOrder = workOrderMapper.selectOne(queryWrapper);
            BigDecimal b = new BigDecimal((float) workOrder.getAlreadyNum() / workOrder.getNeedNum());
            dataCompletionRateVO.setInspectionRate(b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
            count.countDown();
        });
        try {
            count.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return successResult(dataCompletionRateVO);
    }

    @Override
    public ResponseResultVO selectAlarmRecord(PageRO ro) {
        Page page = new Page<Map<String, Object>>(ro.getCurrentPage(), ro.getPageSize());
        Page selectPage = workOrderMapper.selectPage(page, new QueryWrapper<WorkOrder>().lambda().in(WorkOrder::getType, 2, 5).orderByDesc(WorkOrder::getCreateTime).eq(WorkOrder::getRepairType, 1));
        return resultList(selectPage, ro);
    }

    @Override
    public ResponseResultVO assignUser(assignRO ro) {
        WorkOrder workOrder = new WorkOrder();
        BeanUtils.copyProperties(ro, workOrder);
        return updateResult(workOrderMapper.updateById(workOrder));
    }

    @Override
    public ResponseResultVO realTimeStatistics() {
        List<RealTimeStatisticsVO> result = this.init();
        QueryWrapper<WorkOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.select(
                "type",
                "IFNULL(COUNT(*),0) as alreadyNum"
        );
        queryWrapper.lambda().ge(WorkOrder::getCreateTime, DateUtil.getStartTime("start")).lt(WorkOrder::getCreateTime, DateUtil.getStartTime("end"));
        queryWrapper.lambda().groupBy(WorkOrder::getType).having("type in (2,3,5)");
        List<WorkOrder> workOrder = workOrderMapper.selectList(queryWrapper);
        QueryWrapper<WorkOrder> orderQueryWrapper = new QueryWrapper<>();
        orderQueryWrapper.select(
                "type",
                "sum(case status WHEN 0 then 1  ELSE 0 end) as needNum",
                "sum(case status WHEN 1 then 1 ELSE 0 end) as alreadyNum"
        );
        orderQueryWrapper.lambda().between(WorkOrder::getStatus, 0, 1);
        orderQueryWrapper.lambda().groupBy(WorkOrder::getType).having("type in (2,3,5)");
        List<WorkOrder> workOrders = workOrderMapper.selectList(orderQueryWrapper);
        final CountDownLatch count = new CountDownLatch(result.size());
        for (RealTimeStatisticsVO init : result) {
            ThreadPoolUtil.execute(() -> {
                //redis获取总次数
                String name = OrderTypeEng.getNameById(String.valueOf(init.getType()));
                if (redisUtil.hasKey(name)) {
                    Object o = redisUtil.get(name);
                    init.setTotalNum(Integer.valueOf(String.valueOf(o)));
                } else {
                    //如果缓存为空，则查数据库再插入到redis
                    Integer integer = workOrderMapper.selectCount(new QueryWrapper<WorkOrder>().lambda().eq(WorkOrder::getType, init.getType()));
                    redisUtil.set(name, integer);
                    init.setTotalNum(integer);
                }
                //装配 待处理/未完成 数量
                for (WorkOrder pending : workOrders) {
                    if (pending.getType() == init.getType()) {
                        init.setPendingNum(pending.getNeedNum());
                        init.setIncompleteNum(pending.getAlreadyNum() + pending.getNeedNum());
                        break;
                    }
                }
                //装配今日上报次数
                for (WorkOrder vo : workOrder) {
                    if (init.getType() == vo.getType()) {
                        init.setTodayReportNum(vo.getAlreadyNum());
                        break;
                    }
                }
                count.countDown();
            });
        }
        try {
            count.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return ResponseResultVO.successResult(result);
    }

    @Override
    public ResponseResultVO selectSystemState() {
        ResponseResultVO subSystem = systemApiServiceFeign.selectDictionaryByType("subSystem");
        Map<String, Object> systemData = (HashMap<String, Object>) subSystem.getData();
        List<Dictionary> dictionaries = (List<Dictionary>) systemData.get("list");
        if (CollectionUtils.isNotEmpty(dictionaries)) {
            QueryWrapper<Device> queryWrapper = new QueryWrapper<>();
            queryWrapper.select(
                    "sub_system",
                    "COUNT(*) as num"
            );
            queryWrapper.lambda().eq(Device::getDeviceStatus, DeviceStatusType.FAULT.getId());
            queryWrapper.lambda().groupBy(Device::getSubSystem);
            List<Device> devices = deviceMapper.selectList(queryWrapper);
            for (Dictionary dict : dictionaries) {
                for (Device device : devices) {
                    if (device.getSubSystem().equals(dict.getValue())) {
                        dict.setStatus(DeviceStatusType.FAULT.getId());
                    }
                }
            }
        }
        return successResult(dictionaries);
    }

    @Override
    public ResponseResultVO selectAlarmInformation(PageRO ro) {
        Page page = new Page<Map<String, Object>>(ro.getCurrentPage(), ro.getPageSize());
        QueryWrapper<WorkOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(WorkOrder::getRepairType, 0);
        queryWrapper.lambda().in(WorkOrder::getType, 2, 5);
        queryWrapper.lambda().orderByAsc(WorkOrder::getStatus).orderByDesc(WorkOrder::getType);
        Page selectPage = workOrderMapper.selectPage(page, queryWrapper);
        return ResponseResultVO.resultList(selectPage, ro);
    }


    private List<RealTimeStatisticsVO> init() {
        List<RealTimeStatisticsVO> result = new CopyOnWriteArrayList<>();
        List<Integer> list = Arrays.asList(OrderType.REPAIR.getId(), OrderType.HIDDEN.getId(), OrderType.FIRE.getId());
        for (Integer i : list) {
            RealTimeStatisticsVO statisticsVO = new RealTimeStatisticsVO();
            statisticsVO.setType(i);
            result.add(statisticsVO);
        }
        return result;
    }

}
